package pers.whj.net.local.task;

import pers.whj.net.local.exception.ErrorCodeEnum;
import pers.whj.net.local.exception.WHJException;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class FileTask extends Task {
    public static final int MAX_FILE_COUNTS = 1000;
    public static final int UPLOAD_FILE = 101;
    public static final int DOWNLOAD_FILE = 102;
    public static final int UPLOAD_DIRECTORY = 103;
    public static final int DOWNLOAD_DIRECTORY = 104;

    private File mFile;
    private String mRemoteFilePath;
    private long mLength;
    private long mCurrentLength;
    private long mPreRecordLength;
    private long mPreRecordTime;

    private List<FileTask> mSubTaskList;
    private FileTask mParentTask;
    private int mSubTaskIndex;
    private int mRunningSubTaskIndex;

    public FileTask(int type) {
        super(type);
    }

    public FileTask(int type, String name, int priority) {
        super(type, name, priority);
    }

    @Override
    public void init() {
        super.init();
        mFile = null;
        mRemoteFilePath = "";
        mLength = 0;
        mCurrentLength = 0;
    }

    @Override
    public String getCurrentProgress() {
        if (mCurrentLength == 0) {
            return "0.00%";
        }
        if (mLength == 0 || mLength == mCurrentLength) {
            return "100%";
        }
        float progress = (float) mCurrentLength / mLength;
        String result = String.format("%.2f%%", progress * 100);
        if (subTaskCounts() > 0) {
            result = (mRunningSubTaskIndex + 1) + "/" + subTaskCounts() + "-" + result;
        }
        return result;
    }

    @Override
    public String getAverageSpeed() {
        float speed = 0f;
        if (!isRunning() && mLength > 0 && mCurrentLength == mLength) {
            int time = (int) (getEndTime() - getStartTime());
            speed = (float) mLength / time * 1000;
        }
        return formatSpeed(speed);
    }

    @Override
    public void setStartTime(long startTime) {
        super.setStartTime(startTime);
        if (mParentTask != null) {
            mParentTask.setRunningSubTaskIndex(mSubTaskIndex);
        }
    }

    public File getFile() {
        return mFile;
    }

    public String getRemoteFilePath() {
        return mRemoteFilePath;
    }

    public void setFileAndRemotePath(File file, String remoteFilePath) throws WHJException {
        if (remoteFilePath != null) {
            mRemoteFilePath = remoteFilePath;
        }
        if (file != null) {
            mFile = file;
            if (getType() == UPLOAD_DIRECTORY) {
                prepareUploadDirectory();
            }
            if (getType() == DOWNLOAD_DIRECTORY && mFile.isFile()) {
                throw new WHJException(ErrorCodeEnum.FILE_NOT_DIRECTORY);
            }
        }
    }

    public void setRemoteFilePath(String remoteFilePath) {
        if (remoteFilePath != null) {
            if (getType() == UPLOAD_DIRECTORY && mFile != null && mSubTaskList != null) {
                modifySubTaskRemotePath(mRemoteFilePath, remoteFilePath);
            }
            mRemoteFilePath = remoteFilePath;
        }
    }

    public void setLength(long length) {
        if (mParentTask != null) {
            mParentTask.setLength(mParentTask.getLength() - mLength + length);
        }
        mLength = Math.max(length, 0);
    }

    public long getLength() {
        return mLength;
    }

    public void addCurrentLength(long increment) {
        if (mParentTask != null) {
            mParentTask.addCurrentLength(increment);
        }
        if (mCurrentLength == 0) {
            mPreRecordTime = System.currentTimeMillis();
            mPreRecordLength = increment;
            mCurrentSpeed = 0f;
        }
        mCurrentLength += increment;
        long time = System.currentTimeMillis() - mPreRecordTime;
        if (time >= mUpdateSpeedInterval) {
            mCurrentSpeed = (float) (mCurrentLength - mPreRecordLength) / time * 1000;
            mPreRecordTime = System.currentTimeMillis();
            mPreRecordLength = mCurrentLength;
        }
    }

    public long getCurrentLength() {
        return mCurrentLength;
    }

    public FileTask getParentTask() {
        return mParentTask;
    }

    public void setParentTask(FileTask parentTask) {
        mParentTask = parentTask;
    }

    public int getSubTaskIndex() {
        return mSubTaskIndex;
    }

    public void setSubTaskIndex(int subTaskIndex) {
        mSubTaskIndex = subTaskIndex;
    }

    public int getRunningSubTaskIndex() {
        return mRunningSubTaskIndex;
    }

    public void setRunningSubTaskIndex(int runningSubTaskIndex) {
        mRunningSubTaskIndex = runningSubTaskIndex;
    }

    public boolean isSubTask() {
        return mParentTask != null && mParentTask.subTaskCounts() > 0;
    }

    public boolean isLastSubTask() {
        return isSubTask() && (mSubTaskIndex == mParentTask.subTaskCounts() - 1);
    }

    public void onAllSubTaskEnd() {
        boolean success = true;
        if (mSubTaskList != null && mSubTaskList.size() > 0) {
            for (FileTask task : mSubTaskList) {
                if (!task.isSuccess()) {
                    success = false;
                    break;
                }
            }
        }
        setEndTime(System.currentTimeMillis());
        setSuccess(success);
    }

    public int subTaskCounts() {
        return mSubTaskList != null ? mSubTaskList.size() : 0;
    }

    public FileTask getSubTask(int index) {
        return (mSubTaskList != null && index >= 0 && index < mSubTaskList.size()) ? mSubTaskList.get(index) : null;
    }

    public List<FileTask> getSubTaskList() {
        return mSubTaskList;
    }

    public void addDownloadSubTask(FileInfo fileInfo) throws WHJException {
        if (getType() == DOWNLOAD_DIRECTORY) {
            if (fileInfo == null || !fileInfo.isValidFile()) {
                throw new WHJException(ErrorCodeEnum.FILE_NOT_EXIST);
            }
            if (fileInfo.isFile()) {
                throw new WHJException(ErrorCodeEnum.REMOTE_FILE_IS_NOT_DIRECTORY);
            }
            List<FileInfo> fileList = fileInfo.listOnlyFilesInSubPath();
            mSubTaskList = new ArrayList<>();
            String localPathPrefix = mFile.getAbsolutePath() + File.separatorChar;
            for (FileInfo tmp : fileList) {
                if (tmp.isValidFile()) {
                    FileTask task = new FileTask(DOWNLOAD_FILE, getName(), getPriority());
                    File file = new File(localPathPrefix + tmp.getFilePath());
                    task.setFileAndRemotePath(file, tmp.getFilePath());
                    task.setParentTask(this);
                    task.setSubTaskIndex(mSubTaskList.size());
                    task.setLength(tmp.getFileLength());
                    mSubTaskList.add(task);
                }
            }
        }
    }

    private void modifySubTaskRemotePath(String preRemotePath, String currentRemotePath) {
        for (FileTask task : mSubTaskList) {
            String remotePath = task.getRemoteFilePath();
            remotePath = remotePath.replaceFirst(preRemotePath, currentRemotePath);
            task.setRemoteFilePath(remotePath);
        }
    }

    private void prepareUploadDirectory() throws WHJException {
        if (!mFile.exists()) {
            throw new WHJException(ErrorCodeEnum.FILE_NOT_EXIST);
        }
        if (mFile.isFile()) {
            throw new WHJException(ErrorCodeEnum.FILE_NOT_DIRECTORY);
        }
        mSubTaskList = new ArrayList<>();
        addUploadFileSubTask(mFile, mRemoteFilePath + File.separatorChar + mFile.getName());
    }

    private void addUploadFileSubTask(File file, String currentRemotePath) throws WHJException {
        List<File> directoryList = new ArrayList<>();
        File[] allFiles = file.listFiles();
        if (allFiles != null) {
            String nextRemotePathPrefix = currentRemotePath + File.separatorChar;
            for (File tmpFile : allFiles) {
                if (tmpFile.isFile()) {
                    FileTask task = new FileTask(UPLOAD_FILE, getName(), getPriority());
                    task.setFileAndRemotePath(tmpFile, nextRemotePathPrefix + tmpFile.getName());
                    task.setParentTask(this);
                    task.setSubTaskIndex(mSubTaskList.size());
                    task.setLength(tmpFile.length());
                    mSubTaskList.add(task);
                } else {
                    directoryList.add(tmpFile);
                }
            }
            if (mSubTaskList.size() > MAX_FILE_COUNTS) {
                throw new WHJException(ErrorCodeEnum.UPLOAD_DIRECTORY_OVER_LIMIT);
            }
            for (File tmpFile : directoryList) {
                addUploadFileSubTask(tmpFile, nextRemotePathPrefix + tmpFile.getName());
            }
        }
    }
}